# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# (C) 2012 Remek Zajac
#!/usr/bin/python

from lxml import etree
import time as time_module

#local modules
import pyGeometry
import pyPath

class ParseGPX:
    @staticmethod
    def str2timeInSeconds(timeString):
        try:
            time = time_module.strptime(timeString, "%Y-%m-%dT%H:%M:%S.%fZ")
        except ValueError:
            try:
                time = time_module.strptime(timeString, "%Y-%m-%dT%H:%M:%SZ")
            except ValueError:
                time = time_module.strptime(timeString, "%Y-%m-%dT%H:%M:%S")
        return time_module.mktime(time) #convert to seconds    
    
    def __init__(self, gpxFile):
        self.m_gpxFile = gpxFile
        self.m_region = pyGeometry.GeoRegion()
    
    def do(self, callback):
        parsingContext = etree.iterparse( self.m_gpxFile )
        self.m_NodesParsed = 0
        latlon = None
        time = None
        try:
            for event, elem in parsingContext :
                #These are 'stop' events, so collect the time before latlon
                if elem.tag == '{http://www.topografix.com/GPX/1/1}time':
                    time = ParseGPX.str2timeInSeconds(elem.text)                
                if elem.tag == '{http://www.topografix.com/GPX/1/1}trkpt':
                    latlon = (float(elem.get("lat")), float(elem.get("lon")))
                    callback(latlon, time)                        
                    self.m_NodesParsed+=1
                    if (self.m_NodesParsed % 1000 == 0):
                        print "parsing gpx node:", self.m_NodesParsed
                    latlon = None
                    time = None
                else:
                    print "unhandled event:", elem.tag, event                    
                elem.clear()
        except etree.XMLSyntaxError:
            print "some pesky syntax error, bailing"
        del parsingContext



class GPXRenderer(pyPath.Path):
    #gps may be momentarily jumpy and this needs detecting
    IgnoreAccellerationAboveKphs = 100/6 #equal to /seconds to 100kph
    MiniumMeasurementTimespanSec = 5
    MaxSpeedToCountAsStationary = 1

    def __init__(self, parent, gpxFile):
        super(GPXRenderer, self).__init__(parent)
        self.m_gpxFile = gpxFile
        
    def append(self, milestone):    
        if not self.m_lastMilestone:
            return super(GPXRenderer, self).append(milestone)
        milestone.calculateStats(self.m_lastMilestone)
        if (milestone.m_timeFromPrevious < GPXRenderer.MiniumMeasurementTimespanSec or
            milestone.m_accelerationFromPrevious > GPXRenderer.IgnoreAccellerationAboveKphs): #allow for decelleration at any rate.
            milestone.m_reliable = False
            milestone.m_speedFromPrevious = self.m_lastMilestone.m_speedFromPrevious
        if milestone.m_speedFromPrevious < GPXRenderer.MaxSpeedToCountAsStationary:
            #it is useful to recognise stationary
            milestone.m_speedFromPrevious = 0
        return super(GPXRenderer, self).append(milestone)
        
    def parse(self):
        parser = ParseGPX(self.m_gpxFile)
        parser.do(self._parseCallback)
        maxSpeed, avgSpeed, minSpeed = self.speedStats()
        print "maxSpeed:", maxSpeed, "avgSpeed", avgSpeed        

    def _parseCallback(self, latlon, time):
        self.append(pyPath.Path.Milestone(latlon,time))

                    
                
class WriteGpx:
    @staticmethod    
    def timeInSecondsToString(timeInSeconds):
        timeStruct = time_module.gmtime(timeInSeconds)
        timeStr = time_module.strftime("%Y-%m-%dT%H:%M:%S.%fZ", timeStruct)
        return timeStr
        
    def writePreamble(self, f):
        f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
        f.write('<gpx xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd" version="1.1" creator="BTT for Qt" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">\n')
        f.write('    <trk>\n')
        f.write('        <name>Created by GPX experiments</name>\n')
        f.write('        <trkseg>\n')

    def writePostamble(self, f):
        f.write('        </trkseg>\n')
        f.write('    </trk>\n')
        f.write('</gpx>')
                
    def writeTrkPoint(self, milestone, f):
        tagstring = ('            <trkpt lat="'+str(milestone.m_latlon[0])+'" lon="'+str(milestone.m_latlon[1])+'">\n')
        f.write(tagstring)
        if milestone.m_timestamp:
            timetagstring = ('                <time>'+self.timeInSecondsToString(milestone.m_timestamp)+'</time>\n')
            f.write(timetagstring)
        f.write('            </trkpt>\n')
        
    def do(self, path, fileNameOut):
        f = open(fileNameOut, 'w')
        self.writePreamble(f)
        for milestone in path.m_milestones:
            self.writeTrkPoint(milestone, f)
        self.writePostamble(f)
        f.close()
        


        
        
